package com.digua.base.util

import java.math.BigDecimal

object NumUtil {

    /**
     * 获取非空值
     *
     * @param value    原值
     * @param def 默认值
     * @return 如没有默认值，val为空时返回：val；如果有默认值时，为空返回默认值
     */
    fun <T> getVal(value: T?, def: T) = value ?: def

    /**
     * 比较两个数大小
     *
     * @param num1
     * @param num2
     * @param def  当有空值时返回地默认值， 传空时，null值方小
     * @return num1 > num2 => 1; num1 == num2 => 0; num1 < num2 => -1;
     */
    fun <T> compare(num1: T?, num2: T?, def: Int? = null): Int {
        if (num1 == null && num2 == null) return 0
        if (num1 == null) return def ?: -1
        if (num2 == null) return def ?: 1
        var n1 = BigDecimal.ZERO
        var n2 = BigDecimal.ZERO
        try {
            n1 = BigDecimal(num1.toString())
        } catch (e: Exception) {
            LogUtil.e("异常:", e)
        }
        try {
            n2 = BigDecimal(num2.toString())
        } catch (e: Exception) {
            LogUtil.e("异常:", e)
        }
        val value = n1.subtract(n2).toDouble()
        return if (value > 0) 1 else if (value < 0) -1 else 0
    }

    /**
     * 比较两个数是否相等
     *
     * @param num1
     * @param num2
     * @return
     */
    fun <T> ne(num1: T, num2: T) = !eq(num1, num2)

    /**
     * 比较两个数是否相等
     *
     * @param num1
     * @param num2
     * @param def  当有空值时返回地默认值
     * @return
     */
    fun <T> eq(num1: T, num2: T, def: Boolean? = null) = compare<T>(num1, num2, if (def == true) 0 else null) == 0

    /**
     * 比较num1是否大于num2
     *
     * @param num1
     * @param num2
     * @param def  当有空值时返回地默认值
     * @return
     */
    fun <T> gt(num1: T, num2: T, def: Boolean? = null) = compare<T>(num1, num2, if (def == true) null else -1) > 0

    /**
     * 比较num1是否大于等于num2
     *
     * @param num1
     * @param num2
     * @param def  当有空值时返回地默认值
     * @return
     */
    fun <T> gte(num1: T, num2: T, def: Boolean? = null) = compare<T>(num1, num2, if (def == true) null else -1) >= 0

    /**
     * 比较num1是否小于num2
     *
     * @param num1
     * @param num2
     * @param def  当有空值时返回地默认值
     * @return
     */
    fun <T> lt(num1: T, num2: T, def: Boolean? = null) = compare<T>(num1, num2, if (def == true) null else 1) < 0

    /**
     * 比较num1是否小于等于num2
     *
     * @param num1
     * @param num2
     * @param def  当有空值时返回地默认值
     * @return
     */
    fun <T> lte(num1: T, num2: T, def: Boolean? = null) = compare<T>(num1, num2, if (def == true) null else 1) <= 0

    /**
     * 数据转换
     *
     * @param p      传数据
     * @param clz    返回数据类型
     * @param def 默认值
     * @param <R>
     * @param <P>
     * @return
    </P></R> */
    fun <R : Number?, P> parse(p: P?, clz: Class<R>, def: R? = null): R? {
        if (p == null || !ObjUtil.isBaseDataOrStr(p)) return def
        if (p.toString().isEmpty() || p.toString().isEmpty()) return def
        if (ObjUtil.eq(p.javaClass.simpleName, clz.simpleName)) return p as R
        try {
            val value = if (p is String) p else p.toString() + ""
            val num = BigDecimal(value)
            if (ObjUtil.eq(clz.name, Int::class.java.name)) {
                return Integer.valueOf(num.toInt()) as R
            }
            if (ObjUtil.eq(clz.name, Long::class.java.name)) {
                return java.lang.Long.valueOf(num.toLong()) as R
            }
            if (ObjUtil.eq(clz.name, Double::class.java.name)) {
                return java.lang.Double.valueOf(num.toDouble()) as R
            }
            if (ObjUtil.eq(clz.name, Float::class.java.name)) {
                return java.lang.Float.valueOf(num.toFloat()) as R
            }
            if (ObjUtil.eq(clz.name, Byte::class.java.name)) {
                return java.lang.Byte.valueOf(num.toByte()) as R
            }
            if (ObjUtil.eq(clz.name, Short::class.java.name)) {
                return num.toShort() as R
            }
            if (ObjUtil.eq(clz.name, BigDecimal::class.java.name)) {
                return num as R
            }
        } catch (e: Exception) {
            LogUtil.e("异常:", e)
        }
        return def
    }

    fun <P> parseInteger(p: P, def: Int? = null) = parse<Int, P>(p, Int::class.java, def)
    fun <P> parseInt(p: P, def: Int = 0) = parseInteger(p, def) ?: def
    fun <P> parseDouble(p: P, def: Double? = null) = parse<Double, P>(p, Double::class.java, def)
    fun <P> parseDou(p: P, def: Double = 0.0) = parseDouble(p, def) ?: def
    fun <P> parseFloat(p: P, def: Float? = null) = parse<Float, P>(p, Float::class.java, def)
    fun <P> parseFlo(p: P, def: Float = 0F) = parseFloat(p, def) ?: def
    fun <P> parseLong(p: P, def: Long? = null) = parse<Long, P>(p, Long::class.java, def)
    fun <P> parseLon(p: P, def: Long = 0L) = parseLong(p, def) ?: def
    fun <P> parseBigDecimal(p: P, def: BigDecimal? = null) = parse<BigDecimal, P>(p, BigDecimal::class.java, def)

    /**
     * 判断是否是在指定范围内(两边不能等)
     *
     * @param context 上下文
     * @param val     要判断的值
     * @param min     最小值
     * @param max     最大值
     * @param errMsg  不在范围内的错误提示
     * @return
     */
    fun <T : Number?> isRange(value: T?, min: T?, max: T?, errMsg: String? = null): Boolean {
        return checkNum(value, min, max, errMsg) { valueStr, minStr, maxStr ->
            if (lt(minStr, valueStr) && gt(maxStr, valueStr)) true
            else {
                showToast(errMsg)
                false
            }
        }
    }

    /**
     * 判断是否是在指定范围内(两边能等)
     *
     * @param context 上下文
     * @param val     要判断的值
     * @param min     最小值
     * @param max     最大值
     * @param errMsg  不在范围内的错误提示
     * @return
     */
    fun <T : Number?> isRangeEe(value: T?, min: T?, max: T?, errMsg: String? = null): Boolean {
        return checkNum(value, min, max, errMsg) { valueStr, minStr, maxStr ->
            if (lte(minStr, valueStr) && gte(maxStr, valueStr)) true
            else {
                showToast(errMsg)
                false
            }
        }
    }

    /**
     * 判断是否是在指定范围内(前边能等)
     *
     * @param context 上下文
     * @param val     要判断的值
     * @param min     最小值
     * @param max     最大值
     * @param errMsg  不在范围内的错误提示
     * @return
     */
    fun isRangeNe(value: String?, min: String?, max: String?, errMsg: String? = null): Boolean {
        return checkNum(value, min, max, errMsg) { valueStr, minStr, maxStr ->
            if (lte(minStr, valueStr) && gt(maxStr, valueStr)) true
            else {
                showToast(errMsg)
                false
            }
        }
    }

    /**
     * 判断是否是在指定范围内(后边能等)
     *
     * @param context 上下文
     * @param val     要判断的值
     * @param min     最小值
     * @param max     最大值
     * @param errMsg  不在范围内的错误提示
     * @return
     */
    fun <T : Number?> isRangeEn(value: T?, min: T?, max: T?, errMsg: String? = null): Boolean {
        return checkNum(value, min, max, errMsg) { valueStr, minStr, maxStr ->
            if (lt(minStr, valueStr) && gte(maxStr, valueStr)) true
            else {
                showToast(errMsg)
                false
            }
        }
    }

    private fun <T> checkNum(value: T?, min: T?, max: T?, errMsg: String?, lambda: (value: String, min: String, max: String) -> Boolean): Boolean {
        if (value == null || min == null || max == null) return true
        val valueStr = StrUtil.toStr(value)
        val minStr = StrUtil.toStr(min)
        val maxStr = StrUtil.toStr(max)
        if (StrUtil.allBlank(minStr, maxStr)) return true

        if (StrUtil.allBlank(minStr, maxStr)) return true
        if (StrUtil.isBlank(valueStr) && StrUtil.hasNotBlank(maxStr, minStr)) {
            showToast(errMsg)
            return false
        }
        return lambda(valueStr!!, minStr!!, maxStr!!)
    }

    private fun showToast(msg: String? = null) {
        msg?.let {
            if (it.isNotBlank()) {
                Toasts.showLong(msg)
            }
        }
    }

    /**
     * nums中是否包含num
     *
     * @param num
     * @param nums
     * @param <T>
     * @return
    </T> */
    fun <T : Number?> contains(num: T?, vararg nums: T): Boolean {
        if (num == null || nums.isEmpty()) return false
        for (t in nums) {
            if (eq(num, t)) return true
        }
        return false
    }

    fun isNullOr0(num: Number?) = num == null || BigDecimal(num.toString()) == BigDecimal.ZERO

    fun isNotNullOr0(num: Number?) = !isNullOr0(num)

    fun <T1, T2> add(num1: T1?, num2: T2?, def: BigDecimal? = null) = calculator(num1, num2, def) { n1, n2 -> n1.add(n2) }
    fun <T1, T2> subtract(num1: T1?, num2: T2?, def: BigDecimal? = null) = calculator(num1, num2, def) { n1, n2 -> n1.subtract(n2) }
    fun <T1, T2> multiply(num1: T1?, num2: T2?, def: BigDecimal? = null) = calculator(num1, num2, def) { n1, n2 -> n1.multiply(n2) }
    fun <T1, T2> divide(num1: T1?, num2: T2?, def: BigDecimal? = null) = calculator(num1, num2, def) { n1, n2 -> n1.divide(n2) }

    /**
     * 计算
     *
     * @param num1 前一位
     * @param num2 后一位
     * @param <T1> Number或String类型
     * @param <T2> Number或String类型
     * @return 计算结果
    </T2></T1> */
    private fun <T1, T2> calculator(num1: T1?, num2: T2?, def: BigDecimal? = null, lambda: (num1: BigDecimal, num2: BigDecimal) -> BigDecimal): BigDecimal? {
        if (num1 == null || num2 == null) return def
        return try {
            val n1 = BigDecimal(num1.toString())
            val n2 = BigDecimal(num2.toString())
            lambda(n1, n2)
        } catch (e: Exception) {
            def
        }
    }
}
